# scrcpy客户端的源码文件列表
src = [
    'src/main.c',
    'src/adb/adb.c',
    'src/adb/adb_device.c',
    'src/adb/adb_parser.c',
    'src/adb/adb_tunnel.c',
    'src/audio_player.c',
    'src/cli.c',
    'src/clock.c',
    'src/compat.c',
    'src/control_msg.c',
    'src/controller.c',
    'src/decoder.c',
    'src/delay_buffer.c',
    'src/demuxer.c',
    'src/device_msg.c',
    'src/display.c',
    'src/events.c',
    'src/icon.c',
    'src/file_pusher.c',
    'src/fps_counter.c',
    'src/frame_buffer.c',
    'src/input_manager.c',
    'src/keyboard_sdk.c',
    'src/mouse_sdk.c',
    'src/opengl.c',
    'src/options.c',
    'src/packet_merger.c',
    'src/receiver.c',
    'src/recorder.c',
    'src/scrcpy.c',
    'src/screen.c',
    'src/server.c',
    'src/version.c',
    'src/hid/hid_gamepad.c',
    'src/hid/hid_keyboard.c',
    'src/hid/hid_mouse.c',
    'src/trait/frame_source.c',
    'src/trait/packet_source.c',
    'src/uhid/gamepad_uhid.c',
    'src/uhid/keyboard_uhid.c',
    'src/uhid/mouse_uhid.c',
    'src/uhid/uhid_output.c',
    'src/util/acksync.c',
    'src/util/audiobuf.c',
    'src/util/average.c',
    'src/util/file.c',
    'src/util/intmap.c',
    'src/util/intr.c',
    'src/util/log.c',
    'src/util/memory.c',
    'src/util/net.c',
    'src/util/net_intr.c',
    'src/util/process.c',
    'src/util/process_intr.c',
    'src/util/rand.c',
    'src/util/strbuf.c',
    'src/util/str.c',
    'src/util/term.c',
    'src/util/thread.c',
    'src/util/tick.c',
    'src/util/timeout.c',
]

# 测试生成所有的宏展开文件，未完成。这是所有的文件夹目录
header_src=[
    'src',
    'src/adb',
    'src/android',
    'src/sys/unix',
    'src/trait',
    'src/usb',
    'src/util',
]

# 这是scrcpy_app编译的配置,字符串只能使用单引号！
# 这个是注释的示例，下面这句将在终端中打印出一句提示
message('这是 scrcpy client app 编译的配置')

# 获取configuration data对象,包含了生成配置文件的配置的内容。
# meson setup x 命令执行后，会在 x 目录下生成一个config.h文件，conf.set 生成的都是宏定义
conf = configuration_data()

# 常量_POSIX_C_SOURCE及_XOPEN_SOURCE被称为功能测试宏(feature test macro).所有功能测试宏都以下划线开始。当要使用它们时，通常在cc命令行中以下列方式定义。
# _POSIX_C_SOURCE及_XOPEN_SOURCE 都有多个值，对应不同的功能列表
conf.set('_POSIX_C_SOURCE', '200809L')

conf.set('_XOPEN_SOURCE', '700')
#  If _GNU_SOURCE was defined by the user, turn on all the other features.  下面的定义可以打开很多其他特性，注意这几个宏定义的顺序不能改变
conf.set('_GNU_SOURCE', true)

# 下面这句是获取的一个对象，是不能直接进行打印的。参考：https://blog.csdn.net/u010074726/article/details/109010222
# message(host_machine)

# 检查操作系统环境并引入不同的源文件
if host_machine.system() == 'windows'
    windows = import('windows')
    src += [
        'src/sys/win/file.c',
        'src/sys/win/process.c',
        windows.compile_resources('scrcpy-windows.rc'),
    ]
    conf.set('_WIN32_WINNT', '0x0600')
    conf.set('WINVER', '0x0600')
else
    src += [
        'src/sys/unix/file.c',
        'src/sys/unix/process.c',
    ]
    if host_machine.system() == 'darwin'
        conf.set('_DARWIN_C_SOURCE', true)
    endif
endif

# 下面这句中的 get_option('v4l2') 检查的是根目录下meson_option.txt 文件中的配置，有相关的节点
v4l2_support = get_option('v4l2') and host_machine.system() == 'linux'
if v4l2_support
    src += [ 'src/v4l2_sink.c' ]
endif

# 获取根目录下 meson_options.txt 中 usb 配置的值 
usb_support = get_option('usb')
if usb_support
    src += [
        'src/usb/aoa_hid.c',
        'src/usb/gamepad_aoa.c',
        'src/usb/keyboard_aoa.c',
        'src/usb/mouse_aoa.c',
        'src/usb/scrcpy_otg.c',
        'src/usb/screen_otg.c',
        'src/usb/usb.c',
    ]
endif

# 返回compiler对象，该对象代表了指定语言的编译对象，并且可以通过它查询相关属性
cc = meson.get_compiler('c')

dependencies = [
    # dependency对象
    dependency('libavformat', version: '>= 57.33'),
    dependency('libavcodec', version: '>= 57.37'),
    dependency('libavutil'),
    dependency('libswresample'),
    dependency('sdl2', version: '>= 2.0.5'),
]

if v4l2_support
    dependencies += dependency('libavdevice')
endif

if usb_support
    dependencies += dependency('libusb-1.0')
endif

if host_machine.system() == 'windows'
    dependencies += cc.find_library('mingw32')
    dependencies += cc.find_library('ws2_32')
endif

# 将生成自定义的配置到config.h中
check_functions = [
    'strdup',
    'asprintf',
    'vasprintf',
    'nrand48',
    'jrand48',
    'reallocarray',
]

foreach f : check_functions
  if cc.has_function(f)
    define = 'HAVE_' + f.underscorify().to_upper()
    conf.set(define, true)
  endif
endforeach

conf.set('HAVE_SOCK_CLOEXEC', host_machine.system() != 'windows' and
                              cc.has_header_symbol('sys/socket.h', 'SOCK_CLOEXEC'))

# the version, updated on release 定义在根目录下的meson.build中
conf.set_quoted('SCRCPY_VERSION', meson.project_version())

# 可以用下面的方式直接定义 宏是否定义，用于#ifdef检查.NDEBUG不应用此种方式进行定义
# conf.set('NDEBUG', false)

# 路径的前缀，在这个路径下保存有scrcpy的一些信息，比如图标
# the prefix used during configuration (meson --prefix=PREFIX)
conf.set_quoted('PREFIX', get_option('prefix'))

# 编译时是不是将资源文件与可执行文件放在同一个目录下
# build a "portable" version (with scrcpy-server accessible from the same
# directory as the executable)
conf.set('PORTABLE', get_option('portable'))

# the default client TCP port range for the "adb reverse" tunnel
# overridden by option --port
conf.set('DEFAULT_LOCAL_PORT_RANGE_FIRST', '27183')
conf.set('DEFAULT_LOCAL_PORT_RANGE_LAST', '27199')

# run a server debugger and wait for a client to be attached
conf.set('SERVER_DEBUGGER', get_option('server_debugger'))

# select the debugger method ('old' for Android < 9, 'new' for Android >= 9)
conf.set('SERVER_DEBUGGER_METHOD_NEW', get_option('server_debugger_method') == 'new')

# enable V4L2 support (linux only)
conf.set('HAVE_V4L2', v4l2_support)

# enable HID over AOA support (linux only)
conf.set('HAVE_USB', usb_support)

# 生成config.h文件
configure_file(configuration: conf, output: 'config.h')


# 生成项目的时候关闭一些不必要的警告，仅用于测试，正式项目建议打开
warnings = []
warnings += [
    '-Wunused-but-set-variable',
    '-Wunused-label',
  ]
# TODO 为什么没有生效？  
foreach w : warnings
  if not cc.has_argument(w)
    add_project_arguments(w, language: 'c')
  endif
endforeach


src_dir = include_directories('src')

# executable 的说明文档：https://mesonbuild.com/Reference-manual_functions.html#executable
# 指定可执行文件的文件名和入口源文件 exe = executable('main', 'main.c')
executable('scrcpy', src,
           dependencies: dependencies,
           include_directories: src_dir,
           install: true,
           c_args: [])

# <https://mesonbuild.com/Builtin-options.html#directories>
datadir = get_option('datadir') # by default 'share'

install_man('scrcpy.1')
install_data('data/icon.png',
             rename: 'scrcpy.png',
             install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps'))
install_data('data/zsh-completion/_scrcpy',
             install_dir: join_paths(datadir, 'zsh/site-functions'))
install_data('data/bash-completion/scrcpy',
             install_dir: join_paths(datadir, 'bash-completion/completions'))

# Desktop entry file for application launchers
if host_machine.system() == 'linux'
    # Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop)
    install_data('data/scrcpy.desktop',
                 install_dir: join_paths(datadir, 'applications'))
    install_data('data/scrcpy-console.desktop',
                 install_dir: join_paths(datadir, 'applications'))
endif


### TESTS

# 不要在release版本中编译测试模块（断言不会被执行）
# do not build tests in release (assertions would not be executed at all)

if get_option('buildtype') == 'debug'
    tests = [

        # tests/c 文件夹
        ['test_c_file2', [
            'tests/c/test_file.c',
            'tests/common_test.c',
        ]],
        ['test_c_header', [
            'tests/c/test_header.c',
        ]],        
        ['test_c_inline', [
            'tests/c/test_inline.c',
            'tests/common_test.c',
        ]],
        ['test_c_macro', [
            'tests/c/test_macro.c',
            'tests/common_test.c',
        ]],        
        ['test_c_pipe', [
            'tests/c/test_pipe_1.c',
            'tests/common_test.c',
        ]],            
        ['test_c_string', [
            'tests/c/test_string.c',
            'tests/common_test.c',
        ]],

        # tests/sys 文件夹
        # 下面的测试作为例子放在了后面，去本文件下面找就好
        # 编译时不可以有同名的可执行文件，否则会报错
        #['test_sys_system', [
        #    'tests/sys/test_system.c',
        #    'tests/common_test.c',
        #]],

        # tests/util 文件夹
          
        ['test_u_intmap', [
            'tests/util/test_intmap.c',
            'src/util/intmap.c',
            'tests/common_test.c',
        ]],
        ['test_u_log', [
            'tests/util/test_log.c',
            'src/util/log.c',
            'tests/common_test.c',
        ]],
        ['test_u_net', [
            'tests/util/test_net.c',
            'src/util/net.c',
            'tests/common_test.c',
        ]],
        ['test_u_process_intr', [
            'tests/util/test_process_intr.c',
            # 还没有测试通过的测试
            #'src/util/process_intr.c',
            #'src/util/thread.c',
            'tests/common_test.c',
        ]],
        ['test_u_process_unix', [
            'tests/util/test_process_unix.c',
            #'src/util/process.c',
            #'src/util/thread.c',
            'tests/common_test.c',
        ]],
        ['test_u_rand', [
            'tests/util/test_rand.c',
            'src/util/tick.c',
            'src/util/rand.c',
        ]],        
        ['test_u_str', [
            'tests/util/test_str.c',
            'src/util/str.c',
            'src/util/strbuf.c',
        ]],
        ['test_u_strbuf', [
            'tests/util/test_strbuf.c',
            'src/util/strbuf.c',
        ]],
        ['test_u_tick_', [
            # 测试方法的另一个示例
            'tests/util/test_tick.c',
            'tests/common_test.c',
            'src/util/tick.c',
        ]],

        # tests根文件夹
        ['test_adb_device', [
            'src/adb/adb_device.c',
            'tests/common_test.c',
            'tests/test_adb_device.c',
        ]],
        ['test_adb_parser', [
            'tests/test_adb_parser.c',
            'src/adb/adb_device.c',
            'src/adb/adb_parser.c',
            'src/util/str.c',
            'src/util/strbuf.c',
        ]],
        ['test_audiobuf', [
            'tests/test_audiobuf.c',
            'src/util/audiobuf.c',
            'src/util/memory.c',
        ]],
        ['test_binary', [
            'tests/test_binary.c',
        ]],
        ['test_c', [
            'tests/test_c.c',
            #'tests/common_test.c',
        ]],   
        ['test_cli', [
            'tests/test_cli.c',
            'src/cli.c',
            'src/options.c',
            'src/util/log.c',
            'src/util/net.c',
            'src/util/str.c',
            'src/util/strbuf.c',
            'src/util/term.c',
        ]],
        ['test_common', [
            'tests/test_common.c',
        ]],   
        ['test_control_msg_serialize', [
            'tests/test_control_msg_serialize.c',
            'src/control_msg.c',
            'src/util/str.c',
            'src/util/strbuf.c',
        ]],
        ['test_demo', [
            # 这里的头文件并不是必要引入的
            # 测试方法的示例
            'tests/common_test.h',
            'tests/test_demo.c',
            'tests/common_test.c',
        ]],
        ['test_device_msg_deserialize', [
            'tests/test_device_msg_deserialize.c',
            'src/device_msg.c',
        ]],
        ['test_orientation', [
            'tests/test_orientation.c',
            'src/options.c',
        ]],
        ['test_recorder', [
            'tests/test_recorder.c',
            'tests/common_test.c',
        ]],
        ['test_sdl2', [
            'tests/test_sdl2.c',
            'src/version.c',
            'src/opengl.c',
            'tests/common_test.c',
        ]],

        ['test_vecdeque', [
            'tests/test_vecdeque.c',
            'src/util/memory.c',
        ]],
        ['test_vector', [
            'tests/test_vector.c',
        ]],
    ]

    # 有一些测试不是跨平台的
    if host_machine.system() != 'windows'    

        tests += [
            # 本应该在上面定义的，这是一个例子，可以后面添加

            # tests/c 文件夹

            # 为什么下面的测试有时候会不成功？
            ['test_c_FILE', [
                'tests/c/test_FILE_.c',
                'tests/common_test.c',
                'src/util/str.c',
                'src/util/strbuf.c',
            ]],   

            ['test_u_file', [
                'tests/util/test_file.c',
                'src/util/file.c',
                'src/sys/unix/file.c',
                'tests/common_test.c',
            ]],                

            ['test_sys_system', [
                'tests/sys/test_system.c',
                'tests/common_test.c',
            ]],
        ]
    else
        tests += [
            # 本应该在上面定义的，这是一个例子，可以后面添加

            # tests/c 文件夹

            ['test_u_file', [
                'tests/util/test_file.c',
                'src/util/file.c',
                'src/sys/win/file.c',
                'tests/common_test.c',
            ]],            
        ] 
    endif
  
    foreach t : tests
        # t[0] 是单元测试的名称，t[1] 是这个测试需要的一些文件。具体结构见上面的列表
        message('编译单元测试：' + t[0])
        sources = t[1] + ['src/compat.c']

        exe = executable(t[0], sources,
                         include_directories: src_dir,
                         dependencies: dependencies,
                         c_args: ['-DSDL_MAIN_HANDLED', '-DSC_TEST'])
        
        # 下面的三句放开将打印每一个文件
        #foreach test_file:t[1]
        #    message('   需要的文件：' + test_file)
        #endforeach

        # 执行测试                         
        test(t[0], exe)
    endforeach


    # 测试分组测试
    tests = [
        {
            'name':'test_tick',
            'suite':'foo',
            'src':[
                'tests/test_tick.c',
                'src/util/tick.c',
                'tests/common_test.c',
            ]
        },
        {
            'name':'test_log',
            'suite':'foo',
            'src':[
                'tests/test_tick.c',
                'src/util/tick.c',
                'tests/common_test.c',
            ]
        }
    ]
endif
